home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / gnu / groff_src.lha / Groff-1.07 / pic / pic.y < prev    next >
GNU Bison Grammar  |  1992-08-03  |  35KB  |  1,784 lines

  1. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  2.      Written by James Clark (jjc@jclark.com)
  3.  
  4. This file is part of groff.
  5.  
  6. groff is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU General Public License as published by the Free
  8. Software Foundation; either version 2, or (at your option) any later
  9. version.
  10.  
  11. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  12. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14. for more details.
  15.  
  16. You should have received a copy of the GNU General Public License along
  17. with groff; see the file COPYING.  If not, write to the Free Software
  18. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  19. %{
  20. #include "pic.h"
  21. #include "ptable.h"
  22. #include "object.h"
  23.  
  24. extern int delim_flag;
  25. extern void do_copy(const char *);
  26. extern void copy_rest_thru(const char *, const char *);
  27. extern void copy_file_thru(const char *, const char *, const char *);
  28. extern void push_body(const char *);
  29. extern void do_for(char *var, double from, double to,
  30.            int by_is_multiplicative, double by, char *body);
  31. extern void do_lookahead();
  32.  
  33. #undef fmod
  34. #undef rand
  35.  
  36. extern "C" {
  37.   double fmod(double, double);
  38.   int rand();
  39. }
  40.  
  41. /* Maximum number of characters produced by printf("%g") */
  42. #define GDIGITS 14
  43.  
  44. #ifndef __BORLANDC__
  45. #define YYDEBUG 1
  46. #endif /* __BORLANDC__ */
  47.  
  48. int yylex();
  49. void yyerror(const char *);
  50.  
  51. void reset(const char *nm);
  52. void reset_all();
  53.  
  54. place *lookup_label(const char *);
  55. void define_label(const char *label, const place *pl);
  56.  
  57. direction current_direction;
  58. position current_position;
  59.  
  60. implement_ptable(place)
  61.  
  62. PTABLE(place) top_table;
  63.  
  64. PTABLE(place) *current_table = &top_table;
  65. saved_state *current_saved_state = 0;
  66.  
  67. object_list olist;
  68.  
  69. const char *ordinal_postfix(int n);
  70. const char *object_type_name(object_type type);
  71. char *format_number(const char *form, double n);
  72. char *do_sprintf(const char *form, const double *v, int nv);
  73.  
  74. %}
  75.  
  76.  
  77. %union {
  78.     char *str;
  79.     int n;
  80.     double x;
  81.     struct { double x, y; } pair;
  82.     struct { double x; char *body; } if_data;
  83.     struct { char *str; const char *filename; int lineno; } lstr;
  84.     struct { double *v; int nv; int maxv; } dv;
  85.     struct { double val; int is_multiplicative; } by;
  86.     place pl;
  87.     object *obj;
  88.     corner crn;
  89.     path *pth;
  90.     object_spec *spec;
  91.     saved_state *pstate;
  92.     graphics_state state;
  93.     object_type obtype;
  94. }
  95.  
  96. %token <str> LABEL
  97. %token <str> VARIABLE
  98. %token <x> NUMBER
  99. %token <lstr> TEXT
  100. %token <lstr> COMMAND_LINE
  101. %token <str> DELIMITED
  102. %token <n> ORDINAL
  103. %token TH
  104. %token LEFT_ARROW_HEAD
  105. %token RIGHT_ARROW_HEAD
  106. %token DOUBLE_ARROW_HEAD
  107. %token LAST
  108. %token UP
  109. %token DOWN
  110. %token LEFT
  111. %token RIGHT
  112. %token BOX
  113. %token CIRCLE
  114. %token ELLIPSE
  115. %token ARC
  116. %token LINE
  117. %token ARROW
  118. %token MOVE
  119. %token SPLINE
  120. %token HEIGHT
  121. %token RADIUS
  122. %token WIDTH
  123. %token DIAMETER
  124. %token UP
  125. %token DOWN
  126. %token RIGHT
  127. %token LEFT
  128. %token FROM
  129. %token TO
  130. %token AT
  131. %token WITH
  132. %token BY
  133. %token THEN
  134. %token DOTTED
  135. %token DASHED
  136. %token CHOP
  137. %token SAME
  138. %token INVISIBLE
  139. %token LJUST
  140. %token RJUST
  141. %token ABOVE
  142. %token BELOW
  143. %token OF
  144. %token THE
  145. %token WAY
  146. %token BETWEEN
  147. %token AND
  148. %token HERE
  149. %token DOT_N
  150. %token DOT_E    
  151. %token DOT_W
  152. %token DOT_S
  153. %token DOT_NE
  154. %token DOT_SE
  155. %token DOT_NW
  156. %token DOT_SW
  157. %token DOT_C
  158. %token DOT_START
  159. %token DOT_END
  160. %token DOT_X
  161. %token DOT_Y
  162. %token DOT_HT
  163. %token DOT_WID
  164. %token DOT_RAD
  165. %token SIN
  166. %token COS
  167. %token ATAN2
  168. %token LOG
  169. %token EXP
  170. %token SQRT
  171. %token MAX
  172. %token MIN
  173. %token INT
  174. %token RAND
  175. %token COPY
  176. %token THRU
  177. %token TOP
  178. %token BOTTOM
  179. %token UPPER
  180. %token LOWER
  181. %token SH
  182. %token PRINT
  183. %token CW
  184. %token CCW
  185. %token FOR
  186. %token DO
  187. %token IF
  188. %token ELSE
  189. %token ANDAND
  190. %token OROR
  191. %token NOTEQUAL
  192. %token EQUALEQUAL
  193. %token LESSEQUAL
  194. %token GREATEREQUAL
  195. %token LEFT_CORNER
  196. %token RIGHT_CORNER
  197. %token CENTER
  198. %token END
  199. %token START
  200. %token RESET
  201. %token UNTIL
  202. %token PLOT
  203. %token THICKNESS
  204. %token FILL
  205. %token ALIGNED
  206. %token SPRINTF
  207. %token COMMAND
  208.  
  209. %token DEFINE
  210. %token UNDEF
  211.  
  212. /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
  213. %left PLOT
  214. %left TEXT SPRINTF
  215.  
  216. /* give text adjustments higher precedence than TEXT, so that
  217. box "foo" above ljust == box ("foo" above ljust)
  218. */
  219.  
  220. %left LJUST RJUST ABOVE BELOW
  221.  
  222. %left LEFT RIGHT
  223. /* Give attributes that take an optional expression a higher
  224. precedence than left and right, so that eg `line chop left'
  225. parses properly. */
  226. %left CHOP DASHED DOTTED UP DOWN FILL
  227. %left LABEL
  228.  
  229. %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT MAX MIN INT RAND LAST 
  230. %left ORDINAL HERE '`'
  231.  
  232. /* these need to be lower than '-' */
  233. %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
  234.  
  235. /* these must have higher precedence than CHOP so that `label %prec CHOP'
  236. works */
  237. %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
  238. %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
  239. %left UPPER LOWER CENTER START END
  240.  
  241. %left ','
  242. %left OROR
  243. %left ANDAND
  244. %left EQUALEQUAL NOTEQUAL
  245. %left '<' '>' LESSEQUAL GREATEREQUAL
  246.  
  247. %left BETWEEN OF
  248. %left AND
  249.  
  250. %left '+' '-'
  251. %left '*' '/' '%'
  252. %right '!'
  253. %right '^'
  254.  
  255. %type <x> expr any_expr text_expr
  256. %type <by> optional_by
  257. %type <pair> expr_pair position_not_place
  258. %type <if_data> simple_if
  259. %type <obj> nth_primitive
  260. %type <crn> corner
  261. %type <pth> path label_path relative_path
  262. %type <pl> place label element element_list middle_element_list
  263. %type <spec> object_spec
  264. %type <pair> position
  265. %type <obtype> object_type
  266. %type <n> optional_ordinal_last ordinal
  267. %type <str> until
  268. %type <dv> sprintf_args
  269. %type <lstr> text print_args print_arg
  270.  
  271. %%
  272.  
  273. top:
  274.     optional_separator
  275.     | element_list
  276.         {
  277.           if (olist.head)
  278.             print_picture(olist.head);
  279.         }
  280.     ;
  281.  
  282.  
  283. element_list:
  284.     optional_separator middle_element_list optional_separator
  285.         { $$ = $2; }
  286.     ;
  287.  
  288. middle_element_list:
  289.     element
  290.         { $$ = $1; }
  291.     | middle_element_list separator element
  292.         { $$ = $1; }
  293.     ;
  294.  
  295. optional_separator:
  296.     /* empty */
  297.     | separator
  298.     ;
  299.  
  300. separator:
  301.     ';'
  302.     | separator ';'
  303.     ;
  304.  
  305. placeless_element:
  306.     VARIABLE '=' any_expr
  307.         {
  308.           define_variable($1, $3);
  309.           a_delete $1;
  310.         }
  311.     | VARIABLE ':' '=' any_expr
  312.         {
  313.           place *p = lookup_label($1);
  314.           if (!p) {
  315.             lex_error("variable `%1' not defined", $1);
  316.             YYABORT;
  317.           }
  318.           p->obj = 0;
  319.           p->x = $4;
  320.           p->y = 0.0;
  321.           a_delete $1;
  322.         }
  323.     | UP
  324.         { current_direction = UP_DIRECTION; }
  325.     | DOWN
  326.         { current_direction = DOWN_DIRECTION; }
  327.     | LEFT
  328.         { current_direction = LEFT_DIRECTION; }
  329.     | RIGHT
  330.         { current_direction = RIGHT_DIRECTION; }
  331.     | COMMAND_LINE
  332.         {
  333.           olist.append(make_command_object($1.str, $1.filename,
  334.                            $1.lineno));
  335.         }
  336.     | COMMAND print_args
  337.         {
  338.           olist.append(make_command_object($2.str, $2.filename,
  339.                            $2.lineno));
  340.         }
  341.     | PRINT print_args
  342.         {
  343.           fprintf(stderr, "%s\n", $2.str);
  344.               fflush(stderr);
  345.         }
  346.     | SH
  347.         { delim_flag = 1; }
  348.       DELIMITED
  349.         {
  350.           delim_flag = 0;
  351.           system($3);
  352.           a_delete $3;
  353.         }
  354.     | COPY TEXT
  355.         {
  356.           if (yychar < 0)
  357.             do_lookahead();
  358.           do_copy($2.str);
  359.           // do not delete the filename
  360.         }
  361.     | COPY TEXT THRU
  362.         { delim_flag = 2; }
  363.       DELIMITED 
  364.         { delim_flag = 0; }
  365.       until
  366.         {
  367.           if (yychar < 0)
  368.             do_lookahead();
  369.           copy_file_thru($2.str, $5, $7);
  370.           // do not delete the filename
  371.           a_delete $5;
  372.           a_delete $7;
  373.         }
  374.     | COPY THRU
  375.         { delim_flag = 2; }
  376.       DELIMITED
  377.         { delim_flag = 0; }
  378.       until
  379.         {
  380.           if (yychar < 0)
  381.             do_lookahead();
  382.           copy_rest_thru($4, $6);
  383.           a_delete $4;
  384.           a_delete $6;
  385.         }
  386.     | FOR VARIABLE '=' expr TO expr optional_by DO
  387.           { delim_flag = 1; }
  388.       DELIMITED
  389.           {
  390.           delim_flag = 0;
  391.           if (yychar < 0)
  392.             do_lookahead();
  393.           do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10); 
  394.         }
  395.     | simple_if
  396.         {
  397.           if (yychar < 0)
  398.             do_lookahead();
  399.           if ($1.x != 0.0)
  400.             push_body($1.body);
  401.           a_delete $1.body;
  402.         }
  403.     | simple_if ELSE
  404.         { delim_flag = 1; }
  405.       DELIMITED
  406.         {
  407.           delim_flag = 0;
  408.           if (yychar < 0)
  409.             do_lookahead();
  410.           if ($1.x != 0.0)
  411.             push_body($1.body);
  412.           else
  413.             push_body($4);
  414.           a_delete $1.body;
  415.           a_delete $4;
  416.         }
  417.     | reset_variables
  418.     | RESET
  419.         { define_variable("scale", 1.0); }
  420.     ;
  421.  
  422. reset_variables:
  423.     RESET VARIABLE
  424.         { reset($2); a_delete $2; }
  425.     | reset_variables VARIABLE
  426.         { reset($2); a_delete $2; }
  427.     | reset_variables ',' VARIABLE
  428.         { reset($3); a_delete $3; }
  429.     ;
  430.  
  431. print_args:
  432.     print_arg
  433.         { $$ = $1; }
  434.     | print_args print_arg
  435.         {
  436.           $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
  437.           strcpy($$.str, $1.str);
  438.           strcat($$.str, $2.str);
  439.           a_delete $1.str;
  440.           a_delete $2.str;
  441.           if ($1.filename) {
  442.             $$.filename = $1.filename;
  443.             $$.lineno = $1.lineno;
  444.           }
  445.           else if ($2.filename) {
  446.             $$.filename = $2.filename;
  447.             $$.lineno = $2.lineno;
  448.           }
  449.         }
  450.     ;
  451.  
  452. print_arg:
  453.       expr               %prec ','
  454.         {
  455.           $$.str = new char[GDIGITS + 1];
  456.           sprintf($$.str, "%g", $1);
  457.           $$.filename = 0;
  458.           $$.lineno = 0;
  459.         }
  460.     | text
  461.         { $$ = $1; }
  462.     | position          %prec ','
  463.         {
  464.           $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
  465.           sprintf($$.str, "%g, %g", $1.x, $1.y);
  466.           $$.filename = 0;
  467.           $$.lineno = 0;
  468.         }
  469.  
  470. simple_if:
  471.     IF any_expr THEN
  472.         { delim_flag = 1; }
  473.     DELIMITED
  474.         { delim_flag = 0; $$.x = $2; $$.body = $5; }
  475.     ;
  476.  
  477. until:
  478.     /* empty */
  479.         { $$ = 0; }
  480.     | UNTIL TEXT
  481.         { $$ = $2.str; }
  482.     ;
  483.     
  484. any_expr:
  485.     expr
  486.         { $$ = $1; }
  487.     | text_expr
  488.         { $$ = $1; }
  489.     ;
  490.     
  491. text_expr:
  492.     text EQUALEQUAL text
  493.         {
  494.           $$ = strcmp($1.str, $3.str) == 0;
  495.           a_delete $1.str;
  496.           a_delete $3.str;
  497.         }
  498.     | text NOTEQUAL text
  499.         {
  500.           $$ = strcmp($1.str, $3.str) != 0;
  501.           a_delete $1.str;
  502.           a_delete $3.str;
  503.         }
  504.     | text_expr ANDAND text_expr
  505.         { $$ = ($1 != 0.0 && $3 != 0.0); }
  506.     | text_expr ANDAND expr
  507.         { $$ = ($1 != 0.0 && $3 != 0.0); }
  508.     | expr ANDAND text_expr
  509.         { $$ = ($1 != 0.0 && $3 != 0.0); }
  510.     | text_expr OROR text_expr
  511.         { $$ = ($1 != 0.0 || $3 != 0.0); }
  512.     | text_expr OROR expr
  513.         { $$ = ($1 != 0.0 || $3 != 0.0); }
  514.     | expr OROR text_expr
  515.         { $$ = ($1 != 0.0 || $3 != 0.0); }
  516.     | '!' text_expr
  517.         { $$ = ($2 == 0.0); }
  518.     ;
  519.  
  520.  
  521. optional_by:
  522.     /* empty */
  523.         { $$.val = 1.0; $$.is_multiplicative = 0; }
  524.     | BY expr
  525.         { $$.val = $2; $$.is_multiplicative = 0; }
  526.     | BY '*' expr
  527.         { $$.val = $3; $$.is_multiplicative = 1; }
  528.     ;
  529.  
  530. element:
  531.     object_spec
  532.         {
  533.           $$.obj = $1->make_object(¤t_position,
  534.                        ¤t_direction);
  535.           if ($$.obj == 0)
  536.             YYABORT;
  537.           delete $1;
  538.           if ($$.obj)
  539.             olist.append($$.obj);
  540.           else {
  541.             $$.x = current_position.x;
  542.             $$.y = current_position.y;
  543.           }
  544.         }
  545.     | LABEL ':' optional_separator element
  546.         { $$ = $4; define_label($1, & $$); a_delete $1; }
  547.     | LABEL ':' optional_separator position_not_place
  548.         {
  549.           $$.obj = 0;
  550.           $$.x = $4.x;
  551.           $$.y = $4.y;
  552.           define_label($1, & $$);
  553.           a_delete $1;
  554.         }
  555.     | LABEL ':' optional_separator place
  556.         {
  557.           $$ = $4;
  558.           define_label($1, & $$);
  559.           a_delete $1;
  560.         }
  561.     | '{'
  562.         {
  563.           $<state>$.x = current_position.x;
  564.           $<state>$.y = current_position.y;
  565.           $<state>$.dir = current_direction;
  566.         }
  567.       element_list '}'
  568.         {
  569.           current_position.x = $<state>2.x;
  570.           current_position.y = $<state>2.y;
  571.           current_direction = $<state>2.dir;
  572.         }
  573.       optional_element
  574.         {
  575.           $$ = $3;
  576.         }
  577.     | placeless_element
  578.         {
  579.           $$.obj = 0;
  580.           $$.x = current_position.x;
  581.           $$.y = current_position.y;
  582.         }
  583.     ;
  584.  
  585. optional_element:
  586.     /* empty */
  587.         {}
  588.     | element
  589.         {}
  590.     ;
  591.  
  592. object_spec:
  593.     BOX
  594.         {
  595.           $$ = new object_spec(BOX_OBJECT);
  596.         }
  597.     | CIRCLE
  598.         {
  599.           $$ = new object_spec(CIRCLE_OBJECT);
  600.         }
  601.     | ELLIPSE
  602.         {
  603.           $$ = new object_spec(ELLIPSE_OBJECT);
  604.         }
  605.     | ARC
  606.         {
  607.           $$ = new object_spec(ARC_OBJECT);
  608.           $$->dir = current_direction;
  609.         }
  610.     | LINE
  611.         {
  612.           $$ = new object_spec(LINE_OBJECT);
  613.           lookup_variable("lineht", & $$->segment_height);
  614.           lookup_variable("linewid", & $$->segment_width);
  615.           $$->dir = current_direction;
  616.         }
  617.     | ARROW
  618.         {
  619.           $$ = new object_spec(ARROW_OBJECT);
  620.           lookup_variable("lineht", & $$->segment_height);
  621.           lookup_variable("linewid", & $$->segment_width);
  622.           $$->dir = current_direction;
  623.         }
  624.     | MOVE
  625.         {
  626.           $$ = new object_spec(MOVE_OBJECT);
  627.           lookup_variable("moveht", & $$->segment_height);
  628.           lookup_variable("movewid", & $$->segment_width);
  629.           $$->dir = current_direction;
  630.         }
  631.     | SPLINE
  632.         {
  633.           $$ = new object_spec(SPLINE_OBJECT);
  634.           lookup_variable("lineht", & $$->segment_height);
  635.           lookup_variable("linewid", & $$->segment_width);
  636.           $$->dir = current_direction;
  637.         }
  638.     | text   %prec TEXT
  639.         {
  640.           $$ = new object_spec(TEXT_OBJECT);
  641.           $$->text = new text_item($1.str, $1.filename, $1.lineno);
  642.         }
  643.     | PLOT expr
  644.         {
  645.           $$ = new object_spec(TEXT_OBJECT);
  646.           $$->text = new text_item(format_number(0, $2), 0, -1);
  647.         }
  648.     | PLOT expr text
  649.         {
  650.           $$ = new object_spec(TEXT_OBJECT);
  651.           $$->text = new text_item(format_number($3.str, $2),
  652.                        $3.filename, $3.lineno);
  653.           a_delete $3.str;
  654.         }
  655.     | '[' 
  656.         {
  657.           saved_state *p = new saved_state;
  658.           $<pstate>$ = p;
  659.           p->x = current_position.x;
  660.           p->y = current_position.y;
  661.           p->dir = current_direction;
  662.           p->tbl = current_table;
  663.           p->prev = current_saved_state;
  664.           current_position.x = 0.0;
  665.           current_position.y = 0.0;
  666.           current_table = new PTABLE(place);
  667.           current_saved_state = p;
  668.           olist.append(make_mark_object());
  669.         }
  670.       element_list ']'
  671.         {
  672.           current_position.x = $<pstate>2->x;
  673.           current_position.y = $<pstate>2->y;
  674.           current_direction = $<pstate>2->dir;
  675.           $$ = new object_spec(BLOCK_OBJECT);
  676.           olist.wrap_up_block(& $$->oblist);
  677.           $$->tbl = current_table;
  678.           current_table = $<pstate>2->tbl;
  679.           current_saved_state = $<pstate>2->prev;
  680.           delete $<pstate>2;
  681.         }
  682.     | object_spec HEIGHT expr
  683.         {
  684.           $$ = $1;
  685.           $$->height = $3;
  686.           $$->flags |= HAS_HEIGHT;
  687.         }
  688.     | object_spec RADIUS expr
  689.         {
  690.           $$ = $1;
  691.           $$->radius = $3;
  692.           $$->flags |= HAS_RADIUS;
  693.         }
  694.     | object_spec WIDTH expr
  695.         {
  696.           $$ = $1;
  697.           $$->width = $3;
  698.           $$->flags |= HAS_WIDTH;
  699.         }
  700.     | object_spec DIAMETER expr
  701.         {
  702.           $$ = $1;
  703.           $$->radius = $3/2.0;
  704.           $$->flags |= HAS_RADIUS;
  705.         }
  706.     | object_spec expr %prec HEIGHT
  707.         {
  708.           $$ = $1;
  709.           $$->flags |= HAS_SEGMENT;
  710.           switch ($$->dir) {
  711.           case UP_DIRECTION:
  712.             $$->segment_pos.y += $2;
  713.             break;
  714.           case DOWN_DIRECTION:
  715.             $$->segment_pos.y -= $2;
  716.             break;
  717.           case RIGHT_DIRECTION:
  718.             $$->segment_pos.x += $2;
  719.             break;
  720.           case LEFT_DIRECTION:
  721.             $$->segment_pos.x -= $2;
  722.             break;
  723.           }
  724.         }
  725.     | object_spec UP
  726.         {
  727.           $$ = $1;
  728.           $$->dir = UP_DIRECTION;
  729.           $$->flags |= HAS_SEGMENT;
  730.           $$->segment_pos.y += $$->segment_height;
  731.         }
  732.     | object_spec UP expr
  733.         {
  734.           $$ = $1;
  735.           $$->dir = UP_DIRECTION;
  736.           $$->flags |= HAS_SEGMENT;
  737.           $$->segment_pos.y += $3;
  738.         }
  739.     | object_spec DOWN
  740.         {
  741.           $$ = $1;
  742.           $$->dir = DOWN_DIRECTION;
  743.           $$->flags |= HAS_SEGMENT;
  744.           $$->segment_pos.y -= $$->segment_height;
  745.         }
  746.     | object_spec DOWN expr
  747.         {
  748.           $$ = $1;
  749.           $$->dir = DOWN_DIRECTION;
  750.           $$->flags |= HAS_SEGMENT;
  751.           $$->segment_pos.y -= $3;
  752.         }
  753.     | object_spec RIGHT
  754.         {
  755.           $$ = $1;
  756.           $$->dir = RIGHT_DIRECTION;
  757.           $$->flags |= HAS_SEGMENT;
  758.           $$->segment_pos.x += $$->segment_width;
  759.         }
  760.     | object_spec RIGHT expr
  761.         {
  762.           $$ = $1;
  763.           $$->dir = RIGHT_DIRECTION;
  764.           $$->flags |= HAS_SEGMENT;
  765.           $$->segment_pos.x += $3;
  766.         }
  767.     | object_spec LEFT
  768.         {
  769.           $$ = $1;
  770.           $$->dir = LEFT_DIRECTION;
  771.           $$->flags |= HAS_SEGMENT;
  772.           $$->segment_pos.x -= $$->segment_width;
  773.         }
  774.     | object_spec LEFT expr
  775.         {
  776.           $$ = $1;
  777.           $$->dir = LEFT_DIRECTION;
  778.           $$->flags |= HAS_SEGMENT;
  779.           $$->segment_pos.x -= $3;
  780.         }
  781.     | object_spec FROM position
  782.         {
  783.           $$ = $1;
  784.           $$->flags |= HAS_FROM;
  785.           $$->from.x = $3.x;
  786.           $$->from.y = $3.y;
  787.         }
  788.     | object_spec TO position
  789.         {
  790.           $$ = $1;
  791.           if ($$->flags & HAS_SEGMENT)
  792.             $$->segment_list = new segment($$->segment_pos,
  793.                            $$->segment_is_absolute,
  794.                            $$->segment_list);
  795.           $$->flags |= HAS_SEGMENT;
  796.           $$->segment_pos.x = $3.x;
  797.           $$->segment_pos.y = $3.y;
  798.           $$->segment_is_absolute = 1;
  799.           $$->flags |= HAS_TO;
  800.           $$->to.x = $3.x;
  801.           $$->to.y = $3.y;
  802.         }
  803.     | object_spec AT position
  804.         {
  805.           $$ = $1;
  806.           $$->flags |= HAS_AT;
  807.           $$->at.x = $3.x;
  808.           $$->at.y = $3.y;
  809.           if ($$->type != ARC_OBJECT) {
  810.             $$->flags |= HAS_FROM;
  811.             $$->from.x = $3.x;
  812.             $$->from.y = $3.y;
  813.           }
  814.         }
  815.     | object_spec WITH path
  816.         {
  817.           $$ = $1;
  818.           $$->flags |= HAS_WITH;
  819.           $$->with = $3;
  820.         }
  821.     | object_spec BY expr_pair
  822.         {
  823.           $$ = $1;
  824.           $$->flags |= HAS_SEGMENT;
  825.           $$->segment_pos.x += $3.x;
  826.           $$->segment_pos.y += $3.y;
  827.         }
  828.     | object_spec THEN
  829.           {
  830.           $$ = $1;
  831.           if ($$->flags & HAS_SEGMENT) {
  832.             $$->segment_list = new segment($$->segment_pos,
  833.                            $$->segment_is_absolute,
  834.                            $$->segment_list);
  835.             $$->flags &= ~HAS_SEGMENT;
  836.             $$->segment_pos.x = $$->segment_pos.y = 0.0;
  837.             $$->segment_is_absolute = 0;
  838.           }
  839.         }
  840.     | object_spec DOTTED
  841.         {
  842.           $$ = $1;
  843.           $$->flags |= IS_DOTTED;
  844.           lookup_variable("dashwid", & $$->dash_width);
  845.         }
  846.     | object_spec DOTTED expr
  847.         {
  848.           $$ = $1;
  849.           $$->flags |= IS_DOTTED;
  850.           $$->dash_width = $3;
  851.         }
  852.     | object_spec DASHED
  853.         {
  854.           $$ = $1;
  855.           $$->flags |= IS_DASHED;
  856.           lookup_variable("dashwid", & $$->dash_width);
  857.         }
  858.     | object_spec DASHED expr
  859.         {
  860.           $$ = $1;
  861.           $$->flags |= IS_DASHED;
  862.           $$->dash_width = $3;
  863.         }
  864.     | object_spec FILL
  865.         {
  866.           $$ = $1;
  867.           $$->flags |= IS_DEFAULT_FILLED;
  868.         }
  869.     | object_spec FILL expr
  870.         {
  871.           $$ = $1;
  872.           $$->flags |= IS_FILLED;
  873.           $$->fill = $3;
  874.         }
  875.     | object_spec CHOP
  876.           {
  877.           $$ = $1;
  878.           // line chop chop means line chop 0 chop 0
  879.           if ($$->flags & IS_DEFAULT_CHOPPED) {
  880.             $$->flags |= IS_CHOPPED;
  881.             $$->flags &= ~IS_DEFAULT_CHOPPED;
  882.             $$->start_chop = $$->end_chop = 0.0;
  883.           }
  884.           else if ($$->flags & IS_CHOPPED) {
  885.             $$->end_chop = 0.0;
  886.           }
  887.           else {
  888.             $$->flags |= IS_DEFAULT_CHOPPED;
  889.           }
  890.         }
  891.     | object_spec CHOP expr
  892.         {
  893.           $$ = $1;
  894.           if ($$->flags & IS_DEFAULT_CHOPPED) {
  895.             $$->flags |= IS_CHOPPED;
  896.             $$->flags &= ~IS_DEFAULT_CHOPPED;
  897.             $$->start_chop = 0.0;
  898.             $$->end_chop = $3;
  899.           }
  900.           else if ($$->flags & IS_CHOPPED) {
  901.             $$->end_chop = $3;
  902.           }
  903.           else {
  904.             $$->start_chop = $$->end_chop = $3;
  905.             $$->flags |= IS_CHOPPED;
  906.           }
  907.         }
  908.     | object_spec SAME
  909.         {
  910.           $$ = $1;
  911.           $$->flags |= IS_SAME;
  912.         }
  913.     | object_spec INVISIBLE
  914.         {
  915.           $$ = $1;
  916.           $$->flags |= IS_INVISIBLE;
  917.         }
  918.     | object_spec LEFT_ARROW_HEAD
  919.         {
  920.           $$ = $1;
  921.           $$->flags |= HAS_LEFT_ARROW_HEAD;
  922.         }
  923.     | object_spec RIGHT_ARROW_HEAD
  924.         {
  925.           $$ = $1;
  926.           $$->flags |= HAS_RIGHT_ARROW_HEAD;
  927.         }
  928.     | object_spec DOUBLE_ARROW_HEAD
  929.         {
  930.           $$ = $1;
  931.           $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
  932.         }
  933.     | object_spec CW
  934.         {
  935.           $$ = $1;
  936.           $$->flags |= IS_CLOCKWISE;
  937.         }
  938.     | object_spec CCW
  939.         {
  940.           $$ = $1;
  941.           $$->flags &= ~IS_CLOCKWISE;
  942.         }
  943.     | object_spec text   %prec TEXT
  944.         {
  945.           $$ = $1;
  946.           for (text_item **p = & $$->text; *p; p = &(*p)->next)
  947.             ;
  948.           *p = new text_item($2.str, $2.filename, $2.lineno);
  949.         }
  950.     | object_spec LJUST
  951.         {
  952.           $$ = $1;
  953.           if ($$->text) {
  954.             for (text_item *p = $$->text; p->next; p = p->next)
  955.               ;
  956.             p->adj.h = LEFT_ADJUST;
  957.           }
  958.         }
  959.     | object_spec RJUST
  960.         {
  961.           $$ = $1;
  962.           if ($$->text) {
  963.             for (text_item *p = $$->text; p->next; p = p->next)
  964.               ;
  965.             p->adj.h = RIGHT_ADJUST;
  966.           }
  967.         }
  968.     | object_spec ABOVE
  969.         {
  970.           $$ = $1;
  971.           if ($$->text) {
  972.             for (text_item *p = $$->text; p->next; p = p->next)
  973.               ;
  974.             p->adj.v = ABOVE_ADJUST;
  975.           }
  976.         }
  977.     | object_spec BELOW
  978.         {
  979.           $$ = $1;
  980.           if ($$->text) {
  981.             for (text_item *p = $$->text; p->next; p = p->next)
  982.               ;
  983.             p->adj.v = BELOW_ADJUST;
  984.           }
  985.         }
  986.     | object_spec THICKNESS expr
  987.         {
  988.           $$ = $1;
  989.           $$->flags |= HAS_THICKNESS;
  990.           $$->thickness = $3;
  991.         }
  992.     | object_spec ALIGNED
  993.         {
  994.           $$ = $1;
  995.           $$->flags |= IS_ALIGNED;
  996.         }
  997.     ;
  998.  
  999. text:
  1000.     TEXT
  1001.         {
  1002.           $$ = $1;
  1003.         }
  1004.     | SPRINTF '(' TEXT sprintf_args ')'
  1005.         {
  1006.           $$.filename = $3.filename;
  1007.           $$.lineno = $3.lineno;
  1008.           $$.str = do_sprintf($3.str, $4.v, $4.nv);
  1009.           a_delete $4.v;
  1010.           a_delete $3.str;
  1011.         }
  1012.     ;
  1013.  
  1014. sprintf_args:
  1015.     /* empty */
  1016.         {
  1017.           $$.v = 0;
  1018.           $$.nv = 0;
  1019.           $$.maxv = 0;
  1020.         }
  1021.     | sprintf_args ',' expr
  1022.         {
  1023.           $$ = $1;
  1024.           if ($$.nv >= $$.maxv) {
  1025.             if ($$.nv == 0) {
  1026.               $$.v = new double[4];
  1027.               $$.maxv = 4;
  1028.             }
  1029.             else {
  1030.               double *oldv = $$.v;
  1031.               $$.maxv *= 2;
  1032.               $$.v = new double[$$.maxv];
  1033.               memcpy($$.v, oldv, $$.nv*sizeof(double));
  1034.               a_delete oldv;
  1035.             }
  1036.           }
  1037.           $$.v[$$.nv] = $3;
  1038.           $$.nv += 1;
  1039.         }
  1040.     ;
  1041.  
  1042. position:
  1043.       position_not_place
  1044.         { $$ = $1; }
  1045.     | place
  1046.           {
  1047.           position pos = $1;
  1048.           $$.x = pos.x;
  1049.           $$.y = pos.y;
  1050.         }
  1051.     ;
  1052.  
  1053. position_not_place:
  1054.     expr_pair
  1055.         { $$ = $1; }
  1056.     | position '+' expr_pair
  1057.         {
  1058.           $$.x = $1.x + $3.x;
  1059.           $$.y = $1.y + $3.y;
  1060.         }
  1061.     | position '-' expr_pair
  1062.         {
  1063.           $$.x = $1.x - $3.x;
  1064.           $$.y = $1.y - $3.y;
  1065.         }
  1066.     | '(' position ',' position ')'
  1067.         {
  1068.           $$.x = $2.x;
  1069.           $$.y = $4.y;
  1070.         }
  1071.     | expr between position AND position
  1072.         {
  1073.           $$.x = (1.0 - $1)*$3.x + $1*$5.x;
  1074.           $$.y = (1.0 - $1)*$3.y + $1*$5.y;
  1075.         }
  1076.     | expr '<' position ',' position '>'
  1077.         {
  1078.           $$.x = (1.0 - $1)*$3.x + $1*$5.x;
  1079.           $$.y = (1.0 - $1)*$3.y + $1*$5.y;
  1080.         }
  1081.     ;
  1082.  
  1083. between:
  1084.     BETWEEN
  1085.     | OF THE WAY BETWEEN
  1086.     ;
  1087.  
  1088. expr_pair:
  1089.     expr ',' expr
  1090.         { $$.x = $1; $$.y = $3; }
  1091.     | '(' expr_pair ')'
  1092.         { $$ = $2; }
  1093.     ;
  1094.  
  1095. place:
  1096.     label  %prec CHOP /* line at A left == line (at A) left */
  1097.         { $$ = $1; }
  1098.     | label corner
  1099.         {
  1100.           path pth($2);
  1101.           if (!pth.follow($1, & $$))
  1102.             YYABORT;
  1103.         }
  1104.     | corner label
  1105.         {
  1106.           path pth($1);
  1107.           if (!pth.follow($2, & $$))
  1108.             YYABORT;
  1109.         }
  1110.     | corner OF label
  1111.         {
  1112.           path pth($1);
  1113.           if (!pth.follow($3, & $$))
  1114.             YYABORT;
  1115.         }
  1116.     | HERE
  1117.         {
  1118.           $$.x = current_position.x;
  1119.           $$.y = current_position.y;
  1120.           $$.obj = 0;
  1121.         }
  1122.     ;
  1123.  
  1124. label:
  1125.     LABEL
  1126.         {
  1127.           place *p = lookup_label($1);
  1128.           if (!p) {
  1129.             lex_error("there is no place `%1'", $1);
  1130.             YYABORT;
  1131.           }
  1132.           $$ = *p;
  1133.           a_delete $1;
  1134.         }
  1135.     | nth_primitive
  1136.         {
  1137.           $$.obj = $1;
  1138.         }
  1139.     | label '.' LABEL
  1140.         {
  1141.           path pth($3);
  1142.           if (!pth.follow($1, & $$))
  1143.             YYABORT;
  1144.         }
  1145.     ;
  1146.  
  1147. ordinal:
  1148.     ORDINAL
  1149.         { $$ = $1; }
  1150.     | '`' any_expr TH
  1151.         {
  1152.           // XXX Check for overflow (and non-integers?).
  1153.           $$ = (int)$2;
  1154.         }
  1155.     ;
  1156.  
  1157. optional_ordinal_last:
  1158.         LAST
  1159.         { $$ = 1; }
  1160.       | ordinal LAST
  1161.         { $$ = $1; }
  1162.     ;
  1163.  
  1164. nth_primitive:
  1165.     ordinal object_type
  1166.         {
  1167.           int count = 0;
  1168.           for (object *p = olist.head; p != 0; p = p->next)
  1169.             if (p->type() == $2 && ++count == $1) {
  1170.               $$ = p;
  1171.               break;
  1172.             }
  1173.           if (p == 0) {
  1174.             lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
  1175.                   object_type_name($2));
  1176.             YYABORT;
  1177.           }
  1178.         }
  1179.     | optional_ordinal_last object_type
  1180.         {
  1181.           int count = 0;
  1182.           for (object *p = olist.tail; p != 0; p = p->prev)
  1183.             if (p->type() == $2 && ++count == $1) {
  1184.               $$ = p;
  1185.               break;
  1186.             }
  1187.           if (p == 0) {
  1188.             lex_error("there is no %1%2 last %3", $1,
  1189.                   ordinal_postfix($1), object_type_name($2));
  1190.             YYABORT;
  1191.           }
  1192.         }
  1193.     ;
  1194.  
  1195. object_type:
  1196.     BOX
  1197.           { $$ = BOX_OBJECT; }
  1198.     | CIRCLE
  1199.         { $$ = CIRCLE_OBJECT; }
  1200.     | ELLIPSE
  1201.         { $$ = ELLIPSE_OBJECT; }
  1202.     | ARC
  1203.         { $$ = ARC_OBJECT; }
  1204.     | LINE
  1205.         { $$ = LINE_OBJECT; }
  1206.     | ARROW
  1207.         { $$ = ARROW_OBJECT; }
  1208.     | SPLINE
  1209.         { $$ = SPLINE_OBJECT; }
  1210.     | '[' ']'
  1211.         { $$ = BLOCK_OBJECT; }
  1212.     | TEXT
  1213.         { $$ = TEXT_OBJECT; }
  1214.     ;
  1215.  
  1216. label_path:
  1217.      '.' LABEL
  1218.         {
  1219.           $$ = new path($2);
  1220.         }
  1221.     | label_path '.' LABEL
  1222.         {
  1223.           $$ = $1;
  1224.           $$->append($3);
  1225.         }
  1226.     ;
  1227.  
  1228. relative_path:
  1229.     corner
  1230.         {
  1231.           $$ = new path($1);
  1232.         }
  1233.     /* give this a lower precedence than LEFT and RIGHT so that
  1234.        [A: box] with .A left == [A: box] with (.A left) */
  1235.  
  1236.       | label_path %prec TEXT
  1237.         {
  1238.           $$ = $1;
  1239.         }
  1240.     | label_path corner
  1241.         {
  1242.           $$ = $1;
  1243.           $$->append($2);
  1244.         }
  1245.     ;
  1246.  
  1247. path:
  1248.     relative_path
  1249.         {
  1250.           $$ = $1;
  1251.         }
  1252.     /* The rest of these rules are a compatibility sop. */
  1253.     | ORDINAL LAST object_type relative_path
  1254.         {
  1255.           lex_warning("`%1%2 last %3' in `with' argument ignored",
  1256.                   $1, ordinal_postfix($1), object_type_name($3));
  1257.           $$ = $4;
  1258.         }
  1259.     | LAST object_type relative_path
  1260.         {
  1261.           lex_warning("`last %1' in `with' argument ignored",
  1262.                   object_type_name($2));
  1263.           $$ = $3;
  1264.         }
  1265.     | ORDINAL object_type relative_path
  1266.         {
  1267.           lex_warning("`%1%2 %3' in `with' argument ignored",
  1268.                   $1, ordinal_postfix($1), object_type_name($2));
  1269.           $$ = $3;
  1270.         }
  1271.     | LABEL relative_path
  1272.         {
  1273.           lex_warning("initial `%1' in `with' argument ignored", $1);
  1274.           a_delete $1;
  1275.           $$ = $2;
  1276.         }
  1277.     ;
  1278.  
  1279. corner:
  1280.     DOT_N
  1281.         { $$ = &object::north; }
  1282.     | DOT_E    
  1283.         { $$ = &object::east; }
  1284.     | DOT_W
  1285.         { $$ = &object::west; }
  1286.     | DOT_S
  1287.         { $$ = &object::south; }
  1288.     | DOT_NE
  1289.         { $$ = &object::north_east; }
  1290.     | DOT_SE
  1291.         { $$ = &object:: south_east; }
  1292.     | DOT_NW
  1293.         { $$ = &object::north_west; }
  1294.     | DOT_SW
  1295.         { $$ = &object::south_west; }
  1296.     | DOT_C
  1297.         { $$ = &object::center; }
  1298.     | DOT_START
  1299.         { $$ = &object::start; }
  1300.     | DOT_END
  1301.         { $$ = &object::end; }
  1302.       | TOP
  1303.         { $$ = &object::north; }
  1304.     | BOTTOM
  1305.         { $$ = &object::south; }
  1306.     | LEFT
  1307.         { $$ = &object::west; }
  1308.     | RIGHT
  1309.         { $$ = &object::east; }
  1310.     | UPPER LEFT
  1311.         { $$ = &object::north_west; }
  1312.     | LOWER LEFT
  1313.         { $$ = &object::south_west; }
  1314.     | UPPER RIGHT
  1315.         { $$ = &object::north_east; }
  1316.     | LOWER RIGHT
  1317.         { $$ = &object::south_east; }
  1318.     | LEFT_CORNER
  1319.         { $$ = &object::west; }
  1320.     | RIGHT_CORNER
  1321.         { $$ = &object::east; }
  1322.     | UPPER LEFT_CORNER
  1323.         { $$ = &object::north_west; }
  1324.     | LOWER LEFT_CORNER
  1325.         { $$ = &object::south_west; }
  1326.     | UPPER RIGHT_CORNER
  1327.         { $$ = &object::north_east; }
  1328.     | LOWER RIGHT_CORNER
  1329.         { $$ = &object::south_east; }
  1330.     | CENTER
  1331.         { $$ = &object::center; }
  1332.     | START
  1333.         { $$ = &object::start; }
  1334.     | END
  1335.         { $$ = &object::end; }
  1336.     ;
  1337.  
  1338. expr:
  1339.     VARIABLE
  1340.         {
  1341.           if (!lookup_variable($1, & $$)) {
  1342.             lex_error("there is no variable `%1'", $1);
  1343.             YYABORT;
  1344.           }
  1345.           a_delete $1;
  1346.         }
  1347.     | NUMBER
  1348.         { $$ = $1; }
  1349.     | place DOT_X
  1350.           {
  1351.           if ($1.obj != 0)
  1352.             $$ = $1.obj->origin().x;
  1353.           else
  1354.             $$ = $1.x;
  1355.         }            
  1356.     | place DOT_Y
  1357.         {
  1358.           if ($1.obj != 0)
  1359.             $$ = $1.obj->origin().y;
  1360.           else
  1361.             $$ = $1.y;
  1362.         }
  1363.     | place DOT_HT
  1364.         {
  1365.           if ($1.obj != 0)
  1366.             $$ = $1.obj->height();
  1367.           else
  1368.             $$ = 0.0;
  1369.         }
  1370.     | place DOT_WID
  1371.         {
  1372.           if ($1.obj != 0)
  1373.             $$ = $1.obj->width();
  1374.           else
  1375.             $$ = 0.0;
  1376.         }
  1377.     | place DOT_RAD
  1378.         {
  1379.           if ($1.obj != 0)
  1380.             $$ = $1.obj->radius();
  1381.           else
  1382.             $$ = 0.0;
  1383.         }
  1384.     | expr '+' expr
  1385.         { $$ = $1 + $3; }
  1386.     | expr '-' expr
  1387.         { $$ = $1 - $3; }
  1388.     | expr '*' expr
  1389.         { $$ = $1 * $3; }
  1390.     | expr '/' expr
  1391.         {
  1392.           if ($3 == 0.0) {
  1393.             lex_error("division by zero");
  1394.             YYABORT;
  1395.           }
  1396.           $$ = $1/$3;
  1397.         }
  1398.     | expr '%' expr
  1399.         {
  1400.           if ($3 == 0.0) {
  1401.             lex_error("modulus by zero");
  1402.             YYABORT;
  1403.           }
  1404.           $$ = fmod($1, $3);
  1405.         }
  1406.     | expr '^' expr
  1407.         {
  1408.           errno = 0;
  1409.           $$ = pow($1, $3);
  1410.           if (errno == EDOM) {
  1411.             lex_error("arguments to `^' operator out of domain");
  1412.             YYABORT;
  1413.           }
  1414.           if (errno == ERANGE) {
  1415.             lex_error("result of `^' operator out of range");
  1416.             YYABORT;
  1417.           }
  1418.         }
  1419.     | '-' expr    %prec '!'
  1420.         { $$ = -$2; }
  1421.     | '(' any_expr ')'
  1422.         { $$ = $2; }
  1423.     | SIN '(' any_expr ')'
  1424.         {
  1425.           errno = 0;
  1426.           $$ = sin($3);
  1427.           if (errno == ERANGE) {
  1428.             lex_error("sin result out of range");
  1429.             YYABORT;
  1430.           }
  1431.         }
  1432.     | COS '(' any_expr ')'
  1433.         {
  1434.           errno = 0;
  1435.           $$ = cos($3);
  1436.           if (errno == ERANGE) {
  1437.             lex_error("cos result out of range");
  1438.             YYABORT;
  1439.           }
  1440.         }
  1441.     | ATAN2 '(' any_expr ',' any_expr ')'
  1442.         {
  1443.           errno = 0;
  1444.           $$ = atan2($3, $5);
  1445.           if (errno == EDOM) {
  1446.             lex_error("atan2 argument out of domain");
  1447.             YYABORT;
  1448.           }
  1449.           if (errno == ERANGE) {
  1450.             lex_error("atan2 result out of range");
  1451.             YYABORT;
  1452.           }
  1453.         }
  1454.     | LOG '(' any_expr ')'
  1455.         {
  1456.           errno = 0;
  1457.           $$ = log10($3);
  1458.           if (errno == ERANGE) {
  1459.             lex_error("log result out of range");
  1460.             YYABORT;
  1461.           }
  1462.         }
  1463.     | EXP '(' any_expr ')'
  1464.         {
  1465.           errno = 0;
  1466.           $$ = pow(10.0, $3);
  1467.           if (errno == ERANGE) {
  1468.             lex_error("exp result out of range");
  1469.             YYABORT;
  1470.           }
  1471.         }
  1472.     | SQRT '(' any_expr ')'
  1473.         {
  1474.           errno = 0;
  1475.           $$ = sqrt($3);
  1476.           if (errno == EDOM) {
  1477.             lex_error("sqrt argument out of domain");
  1478.             YYABORT;
  1479.           }
  1480.         }
  1481.     | MAX '(' any_expr ',' any_expr ')'
  1482.         { $$ = $3 > $5 ? $3 : $5; }
  1483.     | MIN '(' any_expr ',' any_expr ')'
  1484.         { $$ = $3 < $5 ? $3 : $5; }
  1485.     | INT '(' any_expr ')'
  1486.         { $$ = floor($3); }
  1487.     | RAND '(' any_expr ')'
  1488.         { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
  1489.     | RAND '(' ')'
  1490.         {
  1491.           /* return a random number in the range [0,1) */
  1492.           /* portable, but not very random */
  1493.           $$ = (rand() & 0x7fff) / double(0x8000);
  1494.         }
  1495.     | expr '<' expr
  1496.         { $$ = ($1 < $3); }
  1497.     | expr LESSEQUAL expr
  1498.         { $$ = ($1 <= $3); }
  1499.     | expr '>' expr
  1500.         { $$ = ($1 > $3); }
  1501.     | expr GREATEREQUAL expr
  1502.         { $$ = ($1 >= $3); }
  1503.     | expr EQUALEQUAL expr
  1504.         { $$ = ($1 == $3); }
  1505.     | expr NOTEQUAL expr
  1506.         { $$ = ($1 != $3); }
  1507.     | expr ANDAND expr
  1508.         { $$ = ($1 != 0.0 && $3 != 0.0); }
  1509.     | expr OROR expr
  1510.         { $$ = ($1 != 0.0 || $3 != 0.0); }
  1511.     | '!' expr
  1512.         { $$ = ($2 == 0.0); }
  1513.  
  1514.     ;
  1515.  
  1516. %%
  1517.  
  1518. /* bison defines const to be empty unless __STDC__ is defined, which it
  1519. isn't under cfront */
  1520.  
  1521. #ifdef const
  1522. #undef const
  1523. #endif
  1524.  
  1525. static struct {
  1526.   const char *name;
  1527.   double val;
  1528.   int scaled;             // non-zero if val should be multiplied by scale
  1529. } defaults_table[] = {
  1530.   "arcrad", .25, 1,
  1531.   "arrowht", .1, 1,
  1532.   "arrowwid", .05, 1,
  1533.   "circlerad", .25, 1,
  1534.   "boxht", .5, 1,
  1535.   "boxwid", .75, 1,
  1536.   "boxrad", 0.0, 1,
  1537.   "dashwid", .05, 1,
  1538.   "ellipseht", .5, 1,
  1539.   "ellipsewid", .75, 1,
  1540.   "moveht", .5, 1,
  1541.   "movewid", .5, 1,
  1542.   "lineht", .5, 1,
  1543.   "linewid", .5, 1,
  1544.   "textht", 0.0, 1,
  1545.   "textwid", 0.0, 1,
  1546.   "scale", 1.0, 0,
  1547.   "linethick", -1.0, 0,        // in points
  1548.   "fillval", .5, 0,
  1549.   "arrowhead", 1.0, 0,
  1550.   "maxpswid", 8.5, 0,
  1551.   "maxpsht", 11.0, 0,
  1552. };
  1553.  
  1554. place *lookup_label(const char *label)
  1555. {
  1556.   saved_state *state = current_saved_state;
  1557.   PTABLE(place) *tbl = current_table;
  1558.   for (;;) {
  1559.     place *pl = tbl->lookup(label);
  1560.     if (pl)
  1561.       return pl;
  1562.     if (!state)
  1563.       return 0;
  1564.     tbl = state->tbl;
  1565.     state = state->prev;
  1566.   }
  1567. }
  1568.  
  1569. void define_label(const char *label, const place *pl)
  1570. {
  1571.   place *p = new place;
  1572.   *p = *pl;
  1573.   current_table->define(label, p);
  1574. }
  1575.  
  1576. int lookup_variable(const char *name, double *val)
  1577. {
  1578.   place *pl = lookup_label(name);
  1579.   if (pl) {
  1580.     *val = pl->x;
  1581.     return 1;
  1582.   }
  1583.   return 0;
  1584. }
  1585.  
  1586. void define_variable(const char *name, double val)
  1587. {
  1588.   place *p = new place;
  1589.   p->obj = 0;
  1590.   p->x = val;
  1591.   p->y = 0.0;
  1592.   current_table->define(name, p);
  1593.   if (strcmp(name, "scale") == 0) {
  1594.     // When the scale changes, reset all scaled pre-defined variables to
  1595.     // their default values.
  1596.     for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 
  1597.       if (defaults_table[i].scaled)
  1598.     define_variable(defaults_table[i].name, val*defaults_table[i].val);
  1599.   }
  1600. }
  1601.  
  1602. // called once only (not once per parse)
  1603.  
  1604. void parse_init()
  1605. {
  1606.   current_direction = RIGHT_DIRECTION;
  1607.   current_position.x = 0.0;
  1608.   current_position.y = 0.0;
  1609.   // This resets everything to its default value.
  1610.   reset_all();
  1611. }
  1612.  
  1613. void reset(const char *nm)
  1614. {
  1615.   for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
  1616.     if (strcmp(nm, defaults_table[i].name) == 0) {
  1617.       double val = defaults_table[i].val;
  1618.       if (defaults_table[i].scaled) {
  1619.     double scale;
  1620.     lookup_variable("scale", &scale);
  1621.     val *= scale;
  1622.       }
  1623.       define_variable(defaults_table[i].name, val);
  1624.       return;
  1625.     }
  1626.   lex_error("`%1' is not a predefined variable", nm);
  1627. }
  1628.  
  1629. void reset_all()
  1630. {
  1631.   // We only have to explicitly reset the pre-defined variables that
  1632.   // aren't scaled because `scale' is not scaled, and changing the
  1633.   // value of `scale' will reset all the pre-defined variables that
  1634.   // are scaled.
  1635.   for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
  1636.     if (!defaults_table[i].scaled)
  1637.       define_variable(defaults_table[i].name, defaults_table[i].val);
  1638. }
  1639.  
  1640. // called after each parse
  1641.  
  1642. void parse_cleanup()
  1643. {
  1644.   while (current_saved_state != 0) {
  1645.     delete current_table;
  1646.     current_table = current_saved_state->tbl;
  1647.     saved_state *tem = current_saved_state;
  1648.     current_saved_state = current_saved_state->prev;
  1649.     delete tem;
  1650.   }
  1651.   assert(current_table == &top_table);
  1652.   PTABLE_ITERATOR(place) iter(current_table);
  1653.   const char *key;
  1654.   place *pl;
  1655.   while (iter.next(&key, &pl))
  1656.     if (pl->obj != 0) {
  1657.       position pos = pl->obj->origin();
  1658.       pl->obj = 0;
  1659.       pl->x = pos.x;
  1660.       pl->y = pos.y;
  1661.     }
  1662.   while (olist.head != 0) {
  1663.     object *tem = olist.head;
  1664.     olist.head = olist.head->next;
  1665.     delete tem;
  1666.   }
  1667.   olist.tail = 0;
  1668.   current_direction = RIGHT_DIRECTION;
  1669.   current_position.x = 0.0;
  1670.   current_position.y = 0.0;
  1671. }
  1672.  
  1673. const char *ordinal_postfix(int n)
  1674. {
  1675.   if (n < 10 || n > 20)
  1676.     switch (n % 10) {
  1677.     case 1:
  1678.       return "st";
  1679.     case 2:
  1680.       return "nd";
  1681.     case 3:
  1682.       return "rd";
  1683.     }
  1684.   return "th";
  1685. }
  1686.  
  1687. const char *object_type_name(object_type type)
  1688. {
  1689.   switch (type) {
  1690.   case BOX_OBJECT:
  1691.     return "box";
  1692.   case CIRCLE_OBJECT:
  1693.     return "circle";
  1694.   case ELLIPSE_OBJECT:
  1695.     return "ellipse";
  1696.   case ARC_OBJECT:
  1697.     return "arc";
  1698.   case SPLINE_OBJECT:
  1699.     return "spline";
  1700.   case LINE_OBJECT:
  1701.     return "line";
  1702.   case ARROW_OBJECT:
  1703.     return "arrow";
  1704.   case MOVE_OBJECT:
  1705.     return "move";
  1706.   case TEXT_OBJECT:
  1707.     return "\"\"";
  1708.   case BLOCK_OBJECT:
  1709.     return "[]";
  1710.   case OTHER_OBJECT:
  1711.   case MARK_OBJECT:
  1712.   default:
  1713.     break;
  1714.   }
  1715.   return "object";
  1716. }
  1717.  
  1718. static char sprintf_buf[1024];
  1719.  
  1720. char *format_number(const char *form, double n)
  1721. {
  1722.   if (form == 0)
  1723.     form = "%g";
  1724.   else {
  1725.     // this is a fairly feeble attempt at validation of the format
  1726.     int nspecs = 0;
  1727.     for (const char *p = form; *p != '\0'; p++)
  1728.       if (*p == '%') {
  1729.     if (p[1] == '%')
  1730.       p++;
  1731.     else
  1732.       nspecs++;
  1733.       }
  1734.     if (nspecs > 1) {
  1735.       lex_error("bad format `%1'", form);
  1736.       return strsave(form);
  1737.     }
  1738.   }
  1739.   sprintf(sprintf_buf, form, n);
  1740.   return strsave(sprintf_buf);
  1741. }
  1742.  
  1743. char *do_sprintf(const char *form, const double *v, int nv)
  1744. {
  1745.   string result;
  1746.   int i = 0;
  1747.   string one_format;
  1748.   while (*form) {
  1749.     if (*form == '%') {
  1750.       one_format += *form++;
  1751.       for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
  1752.     one_format += *form;
  1753.       if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
  1754.     lex_error("bad sprintf format");
  1755.     result += one_format;
  1756.     result += form;
  1757.     break;
  1758.       }
  1759.       if (*form == '%') {
  1760.     one_format += *form++;
  1761.     one_format += '\0';
  1762.     sprintf(sprintf_buf, one_format.contents());
  1763.       }
  1764.       else {
  1765.     if (i >= nv) {
  1766.       lex_error("too few arguments to sprintf");
  1767.       result += one_format;
  1768.       result += form;
  1769.       break;
  1770.     }
  1771.     one_format += *form++;
  1772.     one_format += '\0';
  1773.     sprintf(sprintf_buf, one_format.contents(), v[i++]);
  1774.       }
  1775.       one_format.clear();
  1776.       result += sprintf_buf;
  1777.     }
  1778.     else
  1779.       result += *form++;
  1780.   }
  1781.   result += '\0';
  1782.   return strsave(result.contents());
  1783. }
  1784.